package com.htfyun.serialport.uartservo;

import com.htfyun.serialport.utils.SerialDebugLog;
import com.htfyun.serialport.utils.UsefulUtil;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public abstract class IServoReadDataAnalyze<T extends IServoReceivedSerialPacket> {

    private final static boolean ANDROID_DEBUG = false;
    private final static boolean JAVA_DEBUG = false;

    //由于是流数据, 所以有时一次读回来的数据, 并不是完整数据的, 需要结合上下读到的数据来分析是否是我们的包
    private List<ByteBuffer> readDataList = new CopyOnWriteArrayList<>();

    public List<T> analyzeReadData(byte[] rBytes, int length) {

        if (length < 0 || length > rBytes.length) {
            return null;
        }

        byte[] buffer = rBytes;
        if (length < rBytes.length) {
            buffer = new byte[length];
            System.arraycopy(rBytes, 0, buffer, 0, buffer.length);
        }
        ByteBuffer byteBuffer = ByteBuffer.wrap(buffer, 0, buffer.length);
        readDataList.add(byteBuffer);

        return analyze();
    }

    private List<T> analyze() {
        if (readDataList.isEmpty()) {
            return null;
        }

        try {
            //1. 把 readDataList里的数据整合成一个byte[]
            int length = 0;
            for (ByteBuffer buffer : readDataList) {
                length += buffer.limit();
            }
            byte[] tempReadData = new byte[length];
            length = 0;
            for (ByteBuffer buffer : readDataList) {
                buffer.rewind();
                buffer.get(tempReadData, length, buffer.limit());
                length += buffer.limit();
            }

            if (ANDROID_DEBUG) {
                SerialDebugLog.e("tempReadData = " + UsefulUtil.toHexString(tempReadData));
            }

            if (JAVA_DEBUG) {
                System.out.println("tempReadData len = " + tempReadData.length + ", tempReadData = " + UsefulUtil.toHexString(tempReadData));
            }


            PacketAndPosition packetAndPosition = null;
            List<T> packetList = new ArrayList<>();
            int offset = 0;
            do {
                packetAndPosition = doAnalyzeReadData(tempReadData, offset);
                if (packetAndPosition == null ) {
                    continue;
                }

                if (packetAndPosition.position > 0) {
                    offset = packetAndPosition.position;
                }

                if (packetAndPosition.packet != null) {
                    packetList.add(packetAndPosition.packet);
                }

            } while (packetAndPosition != null && packetAndPosition.packet != null);


            if (ANDROID_DEBUG) {
                SerialDebugLog.e("offset =  " + offset);
            }

            if (JAVA_DEBUG) {
                System.out.println("offset =  " + offset);
            }


            readDataList.clear();

            if (offset < tempReadData.length) {
                byte[] newBuffer = new byte[tempReadData.length - offset];
                System.arraycopy(tempReadData, offset, newBuffer, 0, newBuffer.length);
                ByteBuffer byteBuffer = ByteBuffer.wrap(newBuffer);

                if (ANDROID_DEBUG) {
                    SerialDebugLog.e("byteBuffer = " + UsefulUtil.toHexString(byteBuffer.array()));
                }

                if (JAVA_DEBUG) {
                    System.out.println("byteBuffer = " + UsefulUtil.toHexString(byteBuffer.array()));
                }

                readDataList.add(byteBuffer);
            }

            return packetList;

        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;

    }

    private PacketAndPosition doAnalyzeReadData(byte[] tempReadData, int offset) {

        PacketAndPosition packetAndPosition = new PacketAndPosition();
        packetAndPosition.position = -1;
        packetAndPosition.packet = null;

        if (tempReadData == null || tempReadData.length < getMinSizeOfValidPacket() || offset < 0 ) {
            return packetAndPosition;
        }


        int headerPosition = findHeaderPosition(tempReadData, offset);
        if (headerPosition < 0) {
            //没有头的数据, 就抛弃掉.
            packetAndPosition.position = tempReadData.length;
            return packetAndPosition;
        }

        if (ANDROID_DEBUG) {
            SerialDebugLog.e("headerPosition = " + headerPosition + ", offset = " + offset);
        }

        if (JAVA_DEBUG) {
            System.out.println("headerPosition = " + headerPosition + ", offset = " + offset);
        }


        int lengthPosition = headerPosition + positionOfPacketLengthOffsetFromHeader();

        if (lengthPosition + numBytesOfPacketLength() > tempReadData.length ) {

            if (ANDROID_DEBUG) {
                SerialDebugLog.e("lengthPosition + numBytesOfPacketLength() = " + (lengthPosition + numBytesOfPacketLength())
                        + ", tempReadData.length  = " + tempReadData.length);

            }

            if (JAVA_DEBUG) {
                System.out.println("lengthPosition + numBytesOfPacketLength() = " + (lengthPosition + numBytesOfPacketLength())
                        + ", tempReadData.length  = " + tempReadData.length);
            }

            //抛弃头之前的数据
            packetAndPosition.position = headerPosition;
            return packetAndPosition;
        }

        int packageLength = 0;

        if (getByteOrder() == ByteOrder.BIG_ENDIAN) {

            for (int i = 0 ; i < numBytesOfPacketLength(); i++) {
                packageLength |= ((tempReadData[lengthPosition + i] & 0xff) << 8 * (numBytesOfPacketLength() - i));
            }

        } else {

            for (int i = 0 ; i < numBytesOfPacketLength(); i++) {
                packageLength |= ((tempReadData[lengthPosition + i] & 0xff) << 8 * i);
            }

        }

        int packageTotalLength = getExtraLength() + packageLength;

        if (ANDROID_DEBUG) {
            SerialDebugLog.e("packageLen = " + packageLength + ", packageTotalLength = " + packageTotalLength);
        }

        if (JAVA_DEBUG) {
            System.out.println("packageLen = " + packageLength + ", packageTotalLength = " + packageTotalLength);
        }


        //读到的数据包要求的最大bytes数大于我们定义的值.
        //因此抛弃1个header值.
        if (packageTotalLength > getMaxLengthOfPacket()) {
            packetAndPosition.position = headerPosition + 1;
            return packetAndPosition;
        }

        if (headerPosition + packageTotalLength > tempReadData.length) {
            if (ANDROID_DEBUG) {
                SerialDebugLog.e("headerPosition + packageTotalLength = " + (headerPosition + packageTotalLength)
                        + ", packageTotalLength = " + packageTotalLength);

            }

            if (JAVA_DEBUG) {
                System.out.println("headerPosition + packageTotalLength = " + (headerPosition + packageTotalLength)
                        + ", packageTotalLength = " + packageTotalLength);
            }

            //抛弃头之前的数据
            packetAndPosition.position = headerPosition;
            return packetAndPosition;
        }

        ByteBuffer byteBuffer = ByteBuffer.allocate(packageTotalLength);
        byteBuffer.put(tempReadData, headerPosition, byteBuffer.capacity());

        packetAndPosition.packet = createReceivedSerialPacket(byteBuffer);
        if (packetAndPosition.packet.isValid()) {
            packetAndPosition.position = headerPosition + byteBuffer.capacity();
        } else {
            //FD, FD, 01, 07, 00, 00, 00, 02, F5, FD, FD, 01, 07, 00, 00, 00, 00, 00, 02, F5,
            //他收到的第一个包中, 可能丢了一些数据, 但是在下一个包中, 可能仍然有头.
            packetAndPosition.position = headerPosition + 1;

        }
        return packetAndPosition;


    }

    /**
     *
     * @param buffer 查找的 buffer
     * @param offset 从buffer 0位置开始的偏移量
     * @return
     * <p>返回 header 在 byte[] 中的位置</p>
     * <p>返回值小于0, 说明没有HEADER</p>
     *
     */
    private int findHeaderPosition(byte[] buffer, int offset) {

        int position = -1;

        if (getHeader() == null) {
            if (ANDROID_DEBUG) {
                SerialDebugLog.e("getHeader() is null, please check!");
            }
            return position;
        }
        byte[] header = getHeader().array();

        if (header.length == 0) {
            if (ANDROID_DEBUG) {
                SerialDebugLog.e("header length is 0, please check!");
            }
            return position;
        }

        if (offset + header.length > buffer.length) {
            return position;
        }

        for (int pos = offset; pos < buffer.length - header.length; pos++) {
            int i = 0;
            for (i = 0; i < header.length; i++) {
                if (buffer[pos + i] != header[i]) {
                    break;
                }
            }
            if (i >= header.length) {
                position = pos;
                break;
            }
        }

        return position;
    }


    private final class PacketAndPosition {
        T packet;
        int position;
    }


    protected abstract T createReceivedSerialPacket(ByteBuffer byteBuffer);

    protected abstract ByteBuffer getHeader();

    //length 在packet中位置, 从头开始处偏移地址
    protected abstract int positionOfPacketLengthOffsetFromHeader();
    //length 需要几个bytes
    protected abstract int numBytesOfPacketLength();

    protected abstract int getMinSizeOfValidPacket();

    protected abstract int getMaxLengthOfPacket();

    //除去 包中 所说明的 length, 还有额外数据组成一个完整包
    protected abstract int getExtraLength();

    //大端或者小端模式
    protected abstract ByteOrder getByteOrder();
}
